package com.log4jviewer.ui.preferences.filters;

import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.log4jviewer.Activator;
import com.log4jviewer.filters.FilterConstants;
import com.log4jviewer.filters.FilterItemModel;

/**
 * Class represents a part of filters preferences page. It is responsible for building a table, where filter's rules are
 * displayed, and control buttons: Add and Remove to add/remove filter's rules.
 * 
 * @authors <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a></br> <a
 *          href="mailto:Daniil.Yaroslavtsev@gmail.com">Daniil Yaroslavtsev</a>
 */
public class FilterRuleSettings extends AbstractFilterSettings {

    public static final int ENABLE_COLUMN_INDEX = 4;
    public static final int COLUMN_NUMBER = 5;

    private static final Image CHECKED = Activator.getImageDescriptor(
            "icons/checkbox_checked.gif").createImage();

    private static final Image UNCHECKED = Activator.getImageDescriptor(
            "icons/checkbox_unchecked.gif").createImage();

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private FilterContentProvider filterContentProvider;

    private FilterController filterController;

    private TableViewer tableViewer;

    private Button addNewFilterItemButton;

    private Button removeFilterItemButton;
    private Table filterRuleTable;

    public FilterRuleSettings(final FilterContentProvider filterContentProvider,
            final FilterController filterController) {
        this.filterContentProvider = filterContentProvider;
        this.filterController = filterController;
    }

    @Override
    public TableViewer getTableViewer() {
        return tableViewer;
    }

    @Override
    protected void createTableViewer(final Composite composite) {

        tableViewer = new TableViewer(composite, SWT.SINGLE
                | SWT.H_SCROLL
                | SWT.V_SCROLL
                | SWT.BORDER
                | SWT.FULL_SELECTION);

        tableViewer.setUseHashlookup(true);
        tableViewer.setContentProvider(new ArrayContentProvider());

        filterRuleTable = tableViewer.getTable();
        filterRuleTable.setHeaderVisible(true);
        filterRuleTable.setLinesVisible(true);
        GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 17);
        filterRuleTable.setLayoutData(gridData);

        createColumns();

        filterController.setCellEditingStrategy(tableViewer);

        filterRuleTable.addKeyListener(new KeyListener() {

            @Override
            public void keyReleased(final KeyEvent e) {
            }

            @Override
            public void keyPressed(final KeyEvent e) {
                switch (e.keyCode) {
                case SWT.INSERT:
                    doAddNewFilterItemButtonAction();
                    break;
                case SWT.DEL:
                    doRemoveFilterItemButtonAction();
                    break;
                default:
                    break;
                }
            }
        });

        Listener paintListener = new Listener() {
            @Override
            public void handleEvent(final Event event) {

                if ((event.type == SWT.PaintItem) && (event.index == ENABLE_COLUMN_INDEX)) {
                    TableItem item = (TableItem) event.item;
                    Image checkBoxImage = getImage(item.getData());
                    Rectangle imageBounds = checkBoxImage.getBounds();

                    int xOffset = (filterRuleTable.getColumn(ENABLE_COLUMN_INDEX).getWidth()
                            - imageBounds.width) / 2;
                    int yOffset = (item.getBounds().height - imageBounds.height) / 2;

                    event.gc.drawImage(checkBoxImage, event.x + xOffset, event.y + yOffset);
                }
            }
        };

        filterRuleTable.addListener(SWT.PaintItem, paintListener);

        tableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(final SelectionChangedEvent arg0) {
                removeFilterItemButton.setEnabled(true);
            }
        });

        // Selects a full row even when any it`s cell is editing
        filterRuleTable.addListener(SWT.EraseItem, new Listener() {
            @Override
            public void handleEvent(final Event evt) {
                ISelection selection = tableViewer.getSelection();

                if (!selection.isEmpty()) {
                    TableItem eventItem = (TableItem) evt.item;
                    FilterItemModel evtFilterItemModel = (FilterItemModel) eventItem.getData();
                    int selectedItemIndex = filterRuleTable.getSelectionIndex();
                    FilterItemModel selectedFilterItemModel = (FilterItemModel) tableViewer
                            .getElementAt(selectedItemIndex);

                    if (selectedFilterItemModel.equals(evtFilterItemModel)) {
                        evt.detail |= SWT.SELECTED;
                    }
                }
            }
        });

        filterRuleTable.addTraverseListener(new TraverseListener() {
            @Override
            public void keyTraversed(final TraverseEvent e) {
                if (e.detail == SWT.TRAVERSE_TAB_NEXT) {
                    e.doit = false;
                }
            }
        });

    }

    public void setRemoveButtonEnabled(final boolean value) {
        removeFilterItemButton.setEnabled(value);
    }

    @Override
    protected void createColumns() {
        // 'Logic operand' filter's rule column
        final String logicOperandColumnTitle = "Logic operand";
        final int logicOperandColumnWidth = 100;
        TableViewerColumn logicOperandColumn = createSingleColumn(tableViewer,
                logicOperandColumnTitle,
                logicOperandColumnWidth);
        logicOperandColumn.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(final Object element) {
                FilterItemModel filterItemModel = (FilterItemModel) element;
                FilterItemModel firstItem = (FilterItemModel) tableViewer.getElementAt(0);

                String logicOperand = "";

                if (!filterItemModel.equals(firstItem)) {
                    logicOperand = filterItemModel.getLogicOperand().getValue();
                    logger.debug("Filter's rule logic operand: {}", logicOperand);
                }
                return logicOperand;
            }
        });

        logicOperandColumn.setEditingSupport(new RulesLogicCellEditor(tableViewer));

        // 'Field type' filter's rule column
        final String fieldTypeColumnTitle = "Field type";
        final int fieldTypeColumnWidth = 85;
        TableViewerColumn fieldTypeColumn = createSingleColumn(tableViewer,
                fieldTypeColumnTitle,
                fieldTypeColumnWidth);
        fieldTypeColumn.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(final Object element) {
                FilterItemModel filterItemModel = (FilterItemModel) element;
                String fieldType = filterItemModel.getFieldType().getValue();
                logger.debug("Filter's rule field type: {}", fieldType);
                return fieldType;
            }
        });
        fieldTypeColumn.setEditingSupport(new RulesFieldTypeCellEditor(tableViewer));

        // 'Include' filter's rule column
        final String includeColumnTitle = "Include";
        final int includeColumnWidth = 60;
        TableViewerColumn includeColumn = createSingleColumn(tableViewer, includeColumnTitle,
                includeColumnWidth);
        includeColumn.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(final Object element) {
                FilterItemModel filterItemModel = (FilterItemModel) element;
                String filterItemInclude = FilterConstants.EXCLUDE;

                if (filterItemModel.isInclude()) {
                    filterItemInclude = FilterConstants.INCLUDE;
                }
                logger.debug("Filter's rule include: {}", filterItemInclude);
                return filterItemInclude;
            }

        });
        includeColumn.setEditingSupport(new RulesIncludeCellEditor(tableViewer));

        // 'Match pattern' filter's rule column
        final String patternColumnTitle = "Match pattern";
        final int patternColumnWidth = 250;
        TableViewerColumn matchPatternColumn = createSingleColumn(tableViewer,
                patternColumnTitle,
                patternColumnWidth);
        matchPatternColumn.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(final Object element) {
                FilterItemModel filterItemModel = (FilterItemModel) element;
                String matchPattern = filterItemModel.getPattern();
                logger.debug("Filter's rule match pattern: {}", matchPattern);
                return matchPattern;
            }
        });
        matchPatternColumn.setEditingSupport(new RulesMatchPatternCellEditor(tableViewer));

        final String enableColumnTitle = "Enable";
        final int enableColumnWidth = 40;
        TableViewerColumn enableColumn = createSingleColumn(tableViewer, enableColumnTitle,
                enableColumnWidth);
        enableColumn.setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(final Object element) {
                return null;
            }

        });

        enableColumn.setEditingSupport(new RulesEnableCellEditor(tableViewer));

    }

    @Override
    protected void createButtons(final Composite composite) {
        addNewFilterItemButton = createSingleButton(composite, ADD_BUTTON_NAME);
        removeFilterItemButton = createSingleButton(composite, REMOVE_BUTTON_NAME);

        checkAndSetAddButtonState();
        removeFilterItemButton.setEnabled(false);

        addNewFilterItemButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                doAddNewFilterItemButtonAction();
            }
        });

        removeFilterItemButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                doRemoveFilterItemButtonAction();
            }
        });
    }

    public Image getImage(final Object element) {
        Image currentImage;

        if (((FilterItemModel) element).isEnabled()) {
            currentImage = CHECKED;
        } else {
            currentImage = UNCHECKED;
        }
        return currentImage;
    }

    private void doAddNewFilterItemButtonAction() {
        if (filterContentProvider.isSelectedFilterIndexValid()) {
            FilterItemModel filterItemModel = filterContentProvider.createFilterItem();
            tableViewer.add(filterItemModel);
        }
        checkAndSetAddButtonState();
    }

    private void doRemoveFilterItemButtonAction() {
        int selectedFilterItemIndex = filterRuleTable.getSelectionIndex();

        if (selectedFilterItemIndex >= 0) {
            filterContentProvider.removeFilterItem(selectedFilterItemIndex);
            filterRuleTable.remove(selectedFilterItemIndex);
        }
        removeFilterItemButton.setEnabled(false);
    }

    public void checkAndSetAddButtonState() {
        if (filterContentProvider.isFilterListEmpty()) {
            addNewFilterItemButton.setEnabled(false);
        } else {
            addNewFilterItemButton.setEnabled(true);
        }
    }
}
